import {Fragment} from 'react';
import type {RouteComponentProps} from 'react-router';
import styled from '@emotion/styled';
import * as Sentry from '@sentry/react';
import isEqual from 'lodash/isEqual';

import {removeAuthenticator} from 'sentry/actionCreators/account';
import {
  addErrorMessage,
  addLoadingMessage,
  addSuccessMessage,
} from 'sentry/actionCreators/indicator';
import {resendMemberInvite, updateMember} from 'sentry/actionCreators/members';
import {Button} from 'sentry/components/button';
import Confirm from 'sentry/components/confirm';
import DateTime from 'sentry/components/dateTime';
import NotFound from 'sentry/components/errors/notFound';
import FieldGroup from 'sentry/components/forms/fieldGroup';
import HookOrDefault from 'sentry/components/hookOrDefault';
import ExternalLink from 'sentry/components/links/externalLink';
import Panel from 'sentry/components/panels/panel';
import PanelBody from 'sentry/components/panels/panelBody';
import PanelHeader from 'sentry/components/panels/panelHeader';
import PanelItem from 'sentry/components/panels/panelItem';
import {Tooltip} from 'sentry/components/tooltip';
import {IconRefresh} from 'sentry/icons';
import {t, tct} from 'sentry/locale';
import {space} from 'sentry/styles/space';
import type {Member, Organization} from 'sentry/types';
import isMemberDisabledFromLimit from 'sentry/utils/isMemberDisabledFromLimit';
import Teams from 'sentry/utils/teams';
import {normalizeUrl} from 'sentry/utils/withDomainRequired';
import withOrganization from 'sentry/utils/withOrganization';
import type {AsyncViewState} from 'sentry/views/deprecatedAsyncView';
import DeprecatedAsyncView from 'sentry/views/deprecatedAsyncView';
import SettingsPageHeader from 'sentry/views/settings/components/settingsPageHeader';
import TeamSelectForMember from 'sentry/views/settings/components/teamSelect/teamSelectForMember';

import OrganizationRoleSelect from './inviteMember/orgRoleSelect';

const MULTIPLE_ORGS = t('Cannot be reset since user is in more than one organization');
const NOT_ENROLLED = t('Not enrolled in two-factor authentication');
const NO_PERMISSION = t('You do not have permission to perform this action');
const TWO_FACTOR_REQUIRED = t(
  'Cannot be reset since two-factor is required for this organization'
);

type RouteParams = {
  memberId: string;
};

interface Props extends RouteComponentProps<RouteParams, {}> {
  organization: Organization;
}

interface State extends AsyncViewState {
  member: Member | null;
  orgRole: Member['orgRole']; // Form state
  teamRoles: Member['teamRoles']; // Form state
}

const DisabledMemberTooltip = HookOrDefault({
  hookName: 'component:disabled-member-tooltip',
  defaultComponent: ({children}) => <Fragment>{children}</Fragment>,
});

class OrganizationMemberDetail extends DeprecatedAsyncView<Props, State> {
  get hasTeamRoles() {
    const {organization} = this.props;
    return organization.features.includes('team-roles');
  }

  getDefaultState(): State {
    return {
      ...super.getDefaultState(),
      member: null,
      orgRole: '',
      teamRoles: [],
    };
  }

  getEndpoints(): ReturnType<DeprecatedAsyncView['getEndpoints']> {
    const {organization, params} = this.props;
    return [
      ['member', `/organizations/${organization.slug}/members/${params.memberId}/`],
    ];
  }

  onRequestSuccess({data, stateKey}: {data: Member; stateKey: string}) {
    if (stateKey === 'member') {
      const {orgRole, teamRoles} = data;
      this.setState({
        orgRole,
        teamRoles,
      });
    }
  }

  handleSave = async () => {
    const {organization, params} = this.props;
    const {orgRole, teamRoles} = this.state;

    addLoadingMessage(t('Saving...'));
    this.setState({busy: true});

    try {
      const updatedMember = await updateMember(this.api, {
        orgId: organization.slug,
        memberId: params.memberId,
        data: {orgRole, teamRoles} as any,
      });
      this.setState({
        member: updatedMember,
        orgRole: updatedMember.orgRole,
        teamRoles: updatedMember.teamRoles,
        busy: false,
      });
      addSuccessMessage(t('Saved'));
    } catch (resp) {
      const errorMessage = resp?.responseJSON?.detail || t('Could not save...');
      this.setState({busy: false});
      addErrorMessage(errorMessage);
    }
  };

  handleInvite = async (regenerate: boolean) => {
    const {organization, params} = this.props;

    addLoadingMessage(t('Sending invite...'));

    this.setState({busy: true});

    try {
      const data = await resendMemberInvite(this.api, {
        orgId: organization.slug,
        memberId: params.memberId,
        regenerate,
      });

      addSuccessMessage(t('Sent invite!'));

      if (regenerate) {
        this.setState(state => ({member: {...state.member, ...data}}));
      }
    } catch (_err) {
      addErrorMessage(t('Could not send invite'));
    }

    this.setState({busy: false});
  };

  handle2faReset = async () => {
    const {organization, router} = this.props;
    const {user} = this.state.member!;

    const requests =
      user?.authenticators?.map(auth =>
        removeAuthenticator(this.api, user.id, auth.id)
      ) ?? [];

    try {
      await Promise.all(requests);
      router.push(normalizeUrl(`/settings/${organization.slug}/members/`));
      addSuccessMessage(t('All authenticators have been removed'));
    } catch (err) {
      addErrorMessage(t('Error removing authenticators'));
      Sentry.captureException(err);
    }
  };

  onAddTeam = (teamSlug: string) => {
    const teamRoles = [...this.state.teamRoles];
    const i = teamRoles.findIndex(r => r.teamSlug === teamSlug);
    if (i !== -1) {
      return;
    }

    teamRoles.push({teamSlug, role: null});
    this.setState({teamRoles});
  };

  onRemoveTeam = (teamSlug: string) => {
    const teamRoles = this.state.teamRoles.filter(r => r.teamSlug !== teamSlug);
    this.setState({teamRoles});
  };

  onChangeOrgRole = orgRole => this.setState({orgRole});

  onChangeTeamRole = (teamSlug: string, role: string) => {
    if (!this.hasTeamRoles) {
      return;
    }

    const teamRoles = [...this.state.teamRoles];
    const i = teamRoles.findIndex(r => r.teamSlug === teamSlug);
    if (i === -1) {
      return;
    }

    teamRoles[i] = {...teamRoles[i], role};
    this.setState({teamRoles});
  };

  showResetButton = () => {
    const {organization} = this.props;
    const {member} = this.state;
    const {user} = member!;

    if (!user || !user.authenticators || organization.require2FA) {
      return false;
    }
    const hasAuth = user.authenticators.length >= 1;
    return hasAuth && user.canReset2fa;
  };

  getTooltip = (): string => {
    const {organization} = this.props;
    const {member} = this.state;
    const {user} = member!;

    if (!user) {
      return '';
    }

    if (!user.authenticators) {
      return NO_PERMISSION;
    }
    if (!user.authenticators.length) {
      return NOT_ENROLLED;
    }
    if (!user.canReset2fa) {
      return MULTIPLE_ORGS;
    }
    if (organization.require2FA) {
      return TWO_FACTOR_REQUIRED;
    }

    return '';
  };

  get memberDeactivated() {
    return isMemberDisabledFromLimit(this.state.member);
  }

  get hasFormChanged() {
    const {member, orgRole, teamRoles} = this.state;
    if (!member) {
      return false;
    }

    if (orgRole !== member.orgRole || !isEqual(teamRoles, member.teamRoles)) {
      return true;
    }

    return false;
  }

  renderMemberStatus(member: Member) {
    if (this.memberDeactivated) {
      return (
        <em>
          <DisabledMemberTooltip>{t('Deactivated')}</DisabledMemberTooltip>
        </em>
      );
    }
    if (member.expired) {
      return <em>{t('Invitation Expired')}</em>;
    }
    if (member.pending) {
      return <em>{t('Invitation Pending')}</em>;
    }
    return t('Active');
  }

  renderBody() {
    const {organization} = this.props;
    const {member, orgRole, teamRoles} = this.state;

    if (!member) {
      return <NotFound />;
    }

    const {access, orgRoleList} = organization;
    const canEdit = access.includes('org:write') && !this.memberDeactivated;
    const isPartnershipUser = member.flags['partnership:restricted'] === true;

    const {email, expired, pending} = member;
    const canResend = !expired;
    const showAuth = !pending;

    const showResendButton = (member.pending || member.expired) && canResend;

    return (
      <Fragment>
        <SettingsPageHeader
          title={
            <Fragment>
              <div>{member.name}</div>
              <ExtraHeaderText>{t('Member Settings')}</ExtraHeaderText>
            </Fragment>
          }
        />

        <Panel>
          <PanelHeader hasButtons={showResendButton}>
            {t('Basics')}

            {showResendButton && (
              <Button
                data-test-id="resend-invite"
                size="xs"
                priority="primary"
                icon={<IconRefresh />}
                title={t('Generate a new invite link and send a new email.')}
                onClick={() => this.handleInvite(true)}
              >
                {t('Resend Invite')}
              </Button>
            )}
          </PanelHeader>

          <PanelBody>
            <PanelItem>
              <Details>
                <div>
                  <DetailLabel>{t('Email')}</DetailLabel>
                  <div>
                    <ExternalLink href={`mailto:${email}`}>{email}</ExternalLink>
                  </div>
                </div>
                <div>
                  <DetailLabel>{t('Status')}</DetailLabel>
                  <div data-test-id="member-status">
                    {this.renderMemberStatus(member)}
                  </div>
                </div>
                <div>
                  <DetailLabel>{t('Added')}</DetailLabel>
                  <div>
                    <DateTime dateOnly date={member.dateCreated} />
                  </div>
                </div>
              </Details>
            </PanelItem>
          </PanelBody>
        </Panel>

        {showAuth && (
          <Panel>
            <PanelHeader>{t('Authentication')}</PanelHeader>
            <PanelBody>
              <FieldGroup
                alignRight
                flexibleControlStateSize
                label={t('Reset two-factor authentication')}
                help={t(
                  'Resetting two-factor authentication will remove all two-factor authentication methods for this member.'
                )}
              >
                <Tooltip disabled={this.showResetButton()} title={this.getTooltip()}>
                  <Confirm
                    disabled={!this.showResetButton()}
                    message={tct(
                      'Are you sure you want to disable all two-factor authentication methods for [name]?',
                      {name: member.name ? member.name : 'this member'}
                    )}
                    onConfirm={this.handle2faReset}
                  >
                    <Button priority="danger">
                      {t('Reset two-factor authentication')}
                    </Button>
                  </Confirm>
                </Tooltip>
              </FieldGroup>
            </PanelBody>
          </Panel>
        )}

        <OrganizationRoleSelect
          enforceAllowed={false}
          enforceRetired={this.hasTeamRoles}
          disabled={!canEdit || isPartnershipUser}
          roleList={orgRoleList}
          roleSelected={orgRole}
          setSelected={this.onChangeOrgRole}
          helpText={
            isPartnershipUser
              ? t('You cannot make changes to this partner-provisioned user.')
              : undefined
          }
        />

        <Teams slugs={member.teams}>
          {({initiallyLoaded}) => (
            <Fragment>
              <TeamSelectForMember
                disabled={!canEdit}
                organization={organization}
                member={member}
                selectedOrgRole={orgRole}
                selectedTeamRoles={teamRoles}
                onChangeTeamRole={this.onChangeTeamRole}
                onAddTeam={this.onAddTeam}
                onRemoveTeam={this.onRemoveTeam}
                loadingTeams={!initiallyLoaded}
              />
            </Fragment>
          )}
        </Teams>

        <Footer>
          <Button
            priority="primary"
            busy={this.state.busy}
            onClick={this.handleSave}
            disabled={!canEdit || !this.hasFormChanged}
          >
            {t('Save Member')}
          </Button>
        </Footer>
      </Fragment>
    );
  }
}

export default withOrganization(OrganizationMemberDetail);

const ExtraHeaderText = styled('div')`
  color: ${p => p.theme.gray300};
  font-weight: normal;
  font-size: ${p => p.theme.fontSizeLarge};
`;

const Details = styled('div')`
  display: grid;
  grid-auto-flow: column;
  grid-template-columns: 2fr 1fr 1fr;
  gap: ${space(2)};
  width: 100%;

  @media (max-width: ${p => p.theme.breakpoints.small}) {
    grid-auto-flow: row;
    grid-template-columns: auto;
  }
`;

const DetailLabel = styled('div')`
  font-weight: bold;
  margin-bottom: ${space(0.5)};
  color: ${p => p.theme.textColor};
`;

const Footer = styled('div')`
  display: flex;
  justify-content: flex-end;
`;
